#include "TDA_AB.h"
#include <stdio.h>
#include <memory.h>

void reemplazar(TNodoAB **a, TNodoAB **aux)
{

    if ((*a)->der == NULL)
    {

        strcpy( ((TDatoAB*)((*aux)->dato))->dominio,((TDatoAB*)((*a)->dato))->dominio);
        *aux = *a;
        *a = (*a)->izq;
  }
  else
    reemplazar(&(*a)->der, aux);
}


void AB_Crear(TAB *a)
{
    a->raiz = NULL;
    a->cte = NULL;
}

void AB_ElemCte(TAB a, void *dato)
{
    memcpy(dato,a.cte->dato,a.tamDato);

}

void AB_ModifCte(TAB *a, void *dato)
{

    memcpy(a->cte->dato,dato,a->tamDato);

}
TNodoAB* BuscarPadre(TNodoAB *padre,TNodoAB *hijo)
{
    TNodoAB *paux = NULL;

    if ((padre->izq == hijo) || (padre->der == hijo))
        return padre;
    else
    {

        if (padre->izq != NULL)
            paux = BuscarPadre(padre->izq,hijo);

        if ((padre->der != NULL) && (paux == NULL))
            paux = BuscarPadre(padre->der,hijo);

        return paux;
    }
}


void AB_MoverCte(TAB *a,int mov,int *error)
{
    *error = 0;

    switch (mov)
    {

        case IZQ :
            if (a->cte->izq != NULL)
                a->cte = a->cte->izq;

            else
                *error = 1;
            break;

        case DER :
            if (a->cte->der != NULL)
                a->cte = a->cte->der;

            else
                *error = 1;
            break;

        case PAD :
            if (a->cte != a->raiz)
                a->cte = BuscarPadre(a->raiz,a->cte);
            else
                *error = 1;
            break;

        case RAIZ :
            if (a->raiz != NULL)
                a->cte = a->raiz;
            else
                *error = 1;
            break;

        default : *error = 1;
    }
}



void VaciarSub(TNodoAB *pnodo)
{

    if (pnodo != NULL)
    {

        if ((pnodo->izq == NULL) && (pnodo->der == NULL) && (pnodo->dato == NULL ) )
        {

            free(pnodo->dato);
            free(pnodo);
        }
        else
        {

            if ( pnodo->dato != NULL )
                AB_Vaciar(  ((TDatoAB*)(pnodo->dato))->sigNivel  );

            VaciarSub(pnodo->izq);
            VaciarSub(pnodo->der);
            free( ((TDatoAB*)pnodo->dato)->sigNivel);
            free( ((TDatoAB*)pnodo->dato)->server);
            free(pnodo->dato);
            free(pnodo);
        }
    }
}


void AB_Vaciar(TAB *a)
{
    VaciarSub(a->raiz);

    a->raiz = NULL;
    a->cte = NULL;
}


void AB_Insertar(TAB *a,int mov,void *dato,int *error)
{
    TNodoAB *paux;
    *error = 0;

    paux = (TNodoAB*) malloc(sizeof(TNodoAB));
    paux->izq = NULL;
    paux->der = NULL;


    if (paux)
    {
        paux->dato = malloc(a->tamDato);

        if (paux->dato)
        {
            memcpy(paux->dato,dato,a->tamDato);

            switch(mov)
            {
                case IZQ : if (a->cte->izq == NULL)
                                a->cte->izq = paux;
                else
                    *error = 1;
                    break;

                case DER : if (a->cte->der == NULL)
                                a->cte->der = paux;
                else
                    *error = 1;
                    break;

                case RAIZ : if (a->raiz == NULL)
                                a->raiz = paux;
                            else
                                *error = 1;
                                break;
                default : *error = 1;
            }

            if (*error)
            {
                free(paux->dato);
                free(paux);
            }
            else
                a->cte = paux;
        }

        else /* if (paux->dato) */
        {
            *error = 1;
            free(paux);
        }
    }

    else /* if (paux) */
    *error = 1;
}



int AB_Vacio(TAB a)
{
    if (a.raiz == NULL)
        return 1;
    else
        return 0;
}

void ABO_Crear(TAB *a,int (*comp)(void *,void *))
{
    a->raiz = NULL;
    a->cte = NULL;
    a->fcomp = comp;
    a->tamDato = sizeof(TDatoAB);
}

void getDominioFromDato(char *dominio, void *dato){
    strcpy(dominio,((TDatoAB*)dato)->dominio);
}



int buscar_lugar(TAB *a, TNodoAB **p, void *dato)
{
    int c;
    char auxDominio[100]={0};
    char auxDominioDos[100]={0};

    if (*p != NULL)
    {
        getDominioFromDato(auxDominio,(*p)->dato );
        getDominioFromDato(auxDominioDos,dato);
        c = (*(a->fcomp))(auxDominio,auxDominioDos);

        if (c == 1)
            buscar_lugar(a,&((*p)->izq),dato);

        else if (c == -1)
            buscar_lugar(a,&((*p)->der),dato);
        else
            return 1;
    }
    else
    {
        *p = (TNodoAB*) malloc(sizeof(TNodoAB));
        (*p)->izq = NULL;
        (*p)->der = NULL;

        if (*p)
        {
            (*p)->dato = malloc(a->tamDato);

            if ((*p)->dato)
            {
                memcpy((*p)->dato,dato,a->tamDato);
                return 0;
            }
            free(*p);
            return 1;
        }

        return 1;
    }

    return 1;
}

void ABO_Insertar(TAB *a, void *dato, int *error)
{
    *error = buscar_lugar(a,&(a->raiz),dato);
}

void imprimirInOrden(TAB ab, int MOV)
{
    TDatoAB dato;
    int error;

    AB_MoverCte(&ab,MOV,&error);

    if ( !error )
    {

        imprimirInOrden(ab,IZQ);
        AB_ElemCte(ab,&dato);
        imprimirInOrden(ab,DER);
    }

}

int buscarDatoPrimario (TAB *a, TNodoAB **p, void *dato)
{
    int c;
    char auxDominio[100]={0};
    char auxDominioDos[100]={0};

    if (*p){

        getDominioFromDato(auxDominio,(*p)->dato);
        getDominioFromDato(auxDominioDos,dato);
        c = (*(a->fcomp))(auxDominio,auxDominioDos);

        if (c == 1)
            c = buscarDatoPrimario(a,&((*p)->izq),dato);

        else if (c == -1)
            c = buscarDatoPrimario(a,&((*p)->der),dato);

        if ( c == 0 )
            return 0;
    }

    return 1;

}


int obtenerDatos (TAB *a, TNodoAB **p, void *dato)
{
    int c;
    char auxDominio[100]={0};
    char auxDominioDos[100]={0};

    if ( *p ){

        getDominioFromDato(auxDominio,(*p)->dato);
        getDominioFromDato(auxDominioDos,dato);
        c = (*(a->fcomp))(auxDominio,auxDominioDos);

        if (c == 1)
            c = buscarDatoPrimario(a,&((*p)->izq),dato);

        else if (c == -1)
            c = buscarDatoPrimario(a,&((*p)->der),dato);

        if ( c == 0 ){
            AB_ElemCte(*a,dato);
        }
    }
    return 1;
}



int AB_obtenerPosicion (TAB *a, TNodoAB **p, char *dato)
{
    int c;
    char auxDominio[100]={0};

    if (*p){

        getDominioFromDato(auxDominio,(*p)->dato);
        c = (*(a->fcomp))(auxDominio,dato);

        if ( c == 0 ){
            a->cte = (*p);
            return 0;
        }


        if (c == 1)
            c = AB_obtenerPosicion(a,&((*p)->izq),dato);

        if (c == -1)
            c = AB_obtenerPosicion(a,&((*p)->der),dato);

        if ( c == 0 )
            return 0;

    }

    return 1;

}

void borrar(TNodoAB **a, char *elem)
{

    TNodoAB *aux;

    if (*a == NULL)
        return;

    if ( strcmp(  ((TDatoAB*)((*a)->dato))->dominio  ,elem) < 0 ){

        borrar(&(*a)->der, elem);
    }
    else if ( strcmp( ((TDatoAB*)((*a)->dato))->dominio ,elem) > 0 ) {
        borrar(&(*a)->izq, elem);
  }

    else if (  !strcmp( ((TDatoAB*)((*a)->dato))->dominio,elem) )
    {
        aux = (*a);

        if ((*a)->izq == NULL)
            *a = (*a)->der;

        else if ((*a)->der == NULL)
            *a = (*a)->izq;

        else
            reemplazar(&(*a)->izq, &aux);

        if ( ((TDatoAB*)(aux->dato))->sigNivel != NULL)
            AB_Vaciar( ((TDatoAB*)(aux->dato))->sigNivel );

        free( ((TDatoAB*)(aux->dato))->sigNivel );
        free( aux );
    }
}

void AB_BorrarElem(TAB *ab, char* elem){

    int error;

    AB_MoverCte(ab,RAIZ,&error);
    borrar( &(ab->cte),elem);

}


